import { app, shell, BrowserWindow, ipcMain } from 'electron'
import UDPServer from './udp.server'
import SerialportServer from './serialport.server'
import FramePacker from './FramePacker'
import { jspack } from 'jspack';
// const CoreWindow = require('ee-core/electron/window');

const { promisify } = require('util');
const sleep = promisify(setTimeout);

/**
 * 将传入 Buffer 补齐到固定长度（默认 64 字节）
 * 若超过长度则截断
 * @param {Buffer} buf - 原始 Buffer
 * @param {number} [length=64] - 目标长度
 * @returns {Buffer} - 固定长度 Buffer
 */
function padBuffer(buf, length = 68) {
  if (!Buffer.isBuffer(buf)) {
    buf = Buffer.from(buf);
  }

  // 如果超出长度则截断
  if (buf.length >= length) {
    return buf.subarray(0, length);
  }

  // 创建固定长度的 Buffer 并拷贝原数据
  const result = Buffer.alloc(length, 0);
  buf.copy(result);
  return result;
}



export default class MotorServer {
  private _server: UDPServer | SerialportServer
  private _framePacker: FramePacker
  public mainWindow: BrowserWindow

  constructor(server: UDPServer | SerialportServer, framePacker: FramePacker, mainWindow) {
    this._server = server
    this._framePacker = framePacker
    this.mainWindow = mainWindow
    this.bindEvent()
  }



  /**
   * 不断重试某个 Promise，直到它成功为止
   * @param {Function} fn 返回一个 Promise 的函数
   * @param {number} retryCount 最大重试次数
   * @param {number} delay 重试间隔时间（单位：毫秒）
   */
  async retryUntilSuccess(fn, retryCount = 5, delay = 1000) {
    let attempt = 0;

    while (attempt < retryCount) {
      try {
        return await fn(); // 成功就返回
      } catch (err) {
        attempt++;
        console.warn(`请求失败，第 ${attempt} 次重试...`);

        // 如果重试次数达到最大值，抛出异常
        if (attempt === retryCount) {
          console.error('请求已达最大重试次数，放弃请求');
          throw err;
        }

        // 等待一段时间后再进行重试
        await new Promise(res => setTimeout(res, delay));
      }
    }
  }

  /**
 * Promise 串行请求（失败会自动重试当前请求）
 * @param {Function[]} queue 每个元素是个函数，返回 Promise
 * @param {number} retryCount 最大重试次数
 * @param {number} delay 重试间隔时间（单位：毫秒）
 * @returns {Promise}
 */
  serialRequest(queue, retryCount = 5, delay = 1000) {
    return queue.reduce((prevPromise, curFn) => {
      return prevPromise.then(
        async () => await this.retryUntilSuccess(curFn, retryCount, delay)
      );
    }, Promise.resolve());
  }


  bindEvent() {
    ipcMain.handle('CHECK_LINK', async (event, params) => {

      const frame = this._framePacker.map.CHECK_LINK
      const cmd = frame.cmd
      const identify = Buffer.from([0xFE])
      const CHARACTER = "DETECTION"
      const message = padBuffer(Buffer.from(CHARACTER, "utf8"))
      const buffer = this._framePacker.pack(cmd, identify, message)
      const parser = (msg, rinfo) => {
        const message = this._framePacker.unpack(msg, frame)
        return message
      }
      const result = await this._server.sendAsync(cmd.readUInt16BE(), buffer, { parser })
      return {
        status: result.status === CHARACTER,
      }
    })
    ipcMain.handle('RESET_ECS', async (event, params) => {

      const frame = this._framePacker.map.RESET_ECS
      const cmd = frame.cmd
      const identify = Buffer.from([0xFE])
      const message = padBuffer(Buffer.from([0xAA, 0x55]))
      const buffer = this._framePacker.pack(cmd, identify, message)
      const parser = (msg, rinfo) => {
        const message = this._framePacker.unpack(msg, frame)
        return message
      }
      const result = await this._server.sendAsync(cmd.readUInt16BE(), buffer, { parser })
      return result
    })
    ipcMain.handle('TEST_MODEL', async (event, params) => {

      const frame = this._framePacker.map.TEST_MODEL
      const cmd = frame.cmd
      const identify = Buffer.from([0xFE])
      const message = padBuffer(Buffer.from(jspack.Pack(">B", [params.model])))
      const buffer = this._framePacker.pack(cmd, identify, message)
      const parser = (msg, rinfo) => {
        const message = this._framePacker.unpack(msg, frame)
        return message
      }
      const result = await this._server.sendAsync(cmd.readUInt16BE(), buffer, { parser })
      return result
    })

    ipcMain.handle('POWER', async(event, params) => {

      const message = Buffer.from('ETAG ON', "utf8");
      return  await this._server.send(message)
      // return true
    })

    ipcMain.handle('GET_VERSION', async (event, params) => {

      const frame = this._framePacker.map.GET_VERSION
      const cmd = frame.cmd
      const identify = Buffer.from([0xFE])
      const message = padBuffer(Buffer.from([0x00, 0x00]))
      const buffer = this._framePacker.pack(cmd, identify, message)
      const parser = (msg, rinfo) => {
        const message = this._framePacker.unpack(msg, frame)
        return message
      }
      const result = await this._server.sendAsync(cmd.readUInt16BE(), buffer, { parser })
      return result
    })

    ipcMain.handle('SET_PROTECTION', async (event, params) => {

      const frame = this._framePacker.map.SET_PROTECTION
      const cmd = frame.cmd
      const identify = Buffer.from([0xFE])
      // const message = Buffer.alloc(64, 0x00)
      const message = padBuffer(Buffer.from(jspack.Pack(">BB", [params.id, params.state])))
      const buffer = this._framePacker.pack(cmd, identify, message)
      const parser = (msg, rinfo) => {
        const message = this._framePacker.unpack(msg, frame)
        return message
      }
      const result = await this._server.sendAsync(cmd.readUInt16BE(), buffer, { parser })
      return result
    })
    ipcMain.handle('SET_PID', async (event, params) => {

      const frame = this._framePacker.map.SET_PID
      const cmd = frame.cmd
      const identify = Buffer.from([0xFE])
      // const message = Buffer.alloc(64, 0x00)
      const message = padBuffer(Buffer.from(jspack.Pack(">Biii", [params.id, params.kp, params.ki, params.kd])))
      const buffer = this._framePacker.pack(cmd, identify, message)
      const parser = (msg, rinfo) => {
        const message = this._framePacker.unpack(msg, frame)
        return message
      }
      const result = await this._server.sendAsync(cmd.readUInt16BE(), buffer, { parser })
      return result
    })


    ipcMain.handle('SET_DAC', async (event, params) => {

      const frame = this._framePacker.map.SET_DAC
      const cmd = frame.cmd
      const identify = Buffer.from([0xFE])
      // const message = Buffer.alloc(64, 0x00)
      const message = padBuffer(Buffer.from(jspack.Pack(">BI", [params.id, params.dac])))
      const buffer = this._framePacker.pack(cmd, identify, message)
      const parser = (msg, rinfo) => {
        const message = this._framePacker.unpack(msg, frame)
        return message
      }
      const result = await this._server.sendAsync(cmd.readUInt16BE(), buffer, { parser })
      return result
    })


    ipcMain.handle('SET_SPEED', async (event, params) => {

      const frame = this._framePacker.map.SET_SPEED
      const cmd = frame.cmd
      const identify = Buffer.from([0xFE])
      // const message = Buffer.alloc(64, 0x00)
      const message = padBuffer(Buffer.from(jspack.Pack(">bb", [params.speedA || 0, params.speedB || 0])))
      const buffer = this._framePacker.pack(cmd, identify, message)
      const parser = (msg, rinfo) => {
        const message = this._framePacker.unpack(msg, frame)
        return message
      }
      const result = await this._server.sendAsync(cmd.readUInt16BE(), buffer, { parser })
      return result
    })


    let updateBuffer = null;
    ipcMain.handle('BEFORE_UPGRADE', async (event, params) => {

      updateBuffer = Buffer.from(params.fileBuffer, 'utf8');
      const uint8Array = new Uint8Array(updateBuffer);
      const frame = this._framePacker.map.BEFORE_UPGRADE
      const cmd = frame.cmd
      const identify = Buffer.from([0xFE])
      const message = padBuffer(Buffer.from(jspack.Pack(">I", [uint8Array.length])))
      const buffer = this._framePacker.pack(cmd, identify, message)
      const parser = (msg, rinfo) => {
        const message = this._framePacker.unpack(msg, frame)
        return message
      }
      const result = await this._server.sendAsync(cmd.readUInt16BE(), buffer, { parser })
      return result
    })



    function chunkBuffer(buffer, chunkSize) {
      const chunks = [];
      let offset = 0;
      while (offset < buffer.length) {
        chunks.push(buffer.slice(offset, offset + chunkSize));
        offset += chunkSize;
      }
      return chunks;
    }


    ipcMain.handle('UPGRADE', async (event, params) => {

      const fileList = chunkBuffer(new Uint8Array(updateBuffer), 64);
      const frame = this._framePacker.map.UPGRADE
      const cmd = frame.cmd
      const identify = Buffer.from([0xFE])

      const parser = (msg, rinfo) => {
        const message = this._framePacker.unpack(msg, frame)
        return message
      }


      const requestQueue = [];
      fileList.map((file, index) => {
        const request = async () => {
          const message = padBuffer(Buffer.concat([Buffer.from(jspack.Pack(">I", [index+1])), file]))


          const buffer = this._framePacker.pack(cmd, identify, message)
          try {
            const result = await this._server.sendAsync(cmd.readUInt16BE(), buffer, { parser })
            await sleep(10);
            console.log("fileList---->",fileList.length, index, JSON.stringify(result), )
            const progress = (index + 1) / fileList.length;
            if (this.mainWindow && !this.mainWindow.isDestroyed()) {
              this.mainWindow.webContents.send('UPLOAD-PROGRESS', progress)
            }
            return result;
          } catch (error) {
            throw Error(error);
          }
        };
        requestQueue.push(request);
      });

      const result = await this.serialRequest(requestQueue);



      return result;
    })


  }
}
